﻿using System;
using System.Net;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;

namespace OpenApi
{
	/// <summary>
	/// 认证操作。
	/// 在方法执行前进行授权认证。
	/// </summary>
	public class AuthOperationInvoker : IOperationInvoker
	{
		private IOperationInvoker m_OldInvoker;
		private OperationDescription m_OperationDescription;
		private IAuthProvider m_Auth;

		public AuthOperationInvoker(OperationDescription operationDescription, IOperationInvoker oldInvoker, IAuthProvider auth)
		{
			this.m_OperationDescription = operationDescription;
			this.m_OldInvoker = oldInvoker;
			this.m_Auth = auth;
		}

		public object[] AllocateInputs()
		{
			return m_OldInvoker.AllocateInputs();
		}

		public object Invoke(object instance, object[] inputs, out object[] outputs)
		{
			outputs = null;
			var webContext = WebOperationContext.Current;
			var incomingRequest = webContext.IncomingRequest;
			try
			{
				// 根据协定操作的特性确定资源公开度
				var attributes = m_OperationDescription.SyncMethod.GetCustomAttributes(typeof(OpenApiSettingAttribute), false);
				OpenApiSettingAttribute setting;
				if (attributes.Length > 0)
				{
					setting = (OpenApiSettingAttribute)attributes[0];
				}
				else
				{
					setting = new OpenApiSettingAttribute();
				}
				// 身份认证
				if (this.m_Auth != null) // 执行认证
				{
					AuthResult authResult = this.m_Auth.Authenticate(webContext, setting.Openness);
					if (!authResult.Success) // 认证失败
					{
						MyLogger.Info("认证失败==" + MyLogger.Json(new { Method = incomingRequest.Method, Uri = incomingRequest.UriTemplateMatch.RequestUri }));

						webContext.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
						return null;
					}
					else // 认证成功
					{
						if (setting.Openness == ResourceOpenness.Private)
						{// 私有资源则附加用户标识
							//查找UserIdParameter参数索引
							var parameters = m_OperationDescription.SyncMethod.GetParameters();
							int indexUserIdParameter = 0;
							foreach (var parameter in parameters)
							{
								if (String.Compare(parameter.Name, setting.UserIdParameter, true) == 0)
								{
									break;
								}
								else
								{
									indexUserIdParameter++;
								}
							}

							if (indexUserIdParameter == parameters.Length)
							{
								MyLogger.Warn("认证通过，但未能找到用户标识参数==" + MyLogger.Json(m_OperationDescription));
							}
							else
							{
								inputs[indexUserIdParameter] = authResult.UserId;
							}
						}
					}
				}
				else // 不用认证
				{
					MyLogger.Debug("not auth");
				}

				return m_OldInvoker.Invoke(instance, inputs, out outputs);
			}
			catch (Exception ex)
			{
				webContext.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
				MyLogger.Fatal("OperationInvoker", ex);
				return null;
			}
		}

		public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
		{
			throw new NotImplementedException();
		}

		public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
		{
			throw new NotImplementedException();
		}

		public bool IsSynchronous
		{
			get { return true; }
		}
	}
}
